# --------------------------------------------------------------------------------
#  NoiseTube Web application
#  
#  Copyright (C) 2008-2010 SONY Computer Science Laboratory Paris
#  Portions contributed by Vrije Universiteit Brussel (BrusSense team), 2008-2011
# --------------------------------------------------------------------------------
#  This library is free software; you can redistribute it and/or modify it under
#  the terms of the GNU Lesser General Public License, version 2.1, as published
#  by the Free Software Foundation.
#  
#  This library is distributed in the hope that it will be useful, but WITHOUT
#  ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS
#  FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public License for more
#  details.
#  
#  You should have received a copy of the GNU Lesser General Public License along
#  with this library; if not, write to:
#    Free Software Foundation, Inc.,
#    51 Franklin Street, Fifth Floor,
#    Boston, MA  02110-1301, USA.
#  
#  Full GNU LGPL v2.1 text: http://www.gnu.org/licenses/old-licenses/lgpl-2.1.txt
#  NoiseTube project source code repository: http://code.google.com/p/noisetube
# --------------------------------------------------------------------------------
#  More information:
#   - NoiseTube project website: http://www.noisetube.net
#   - Sony Computer Science Laboratory Paris: http://csl.sony.fr
#   - VUB BrusSense team: http://www.brussense.be
# --------------------------------------------------------------------------------
 
class City < ActiveRecord::Base
  has_many :city_names
  has_many :tracks
  #has_many :urbanelements
  has_many :users, :through => :tracks, :uniq=>true
  has_many :measures,:through => :tracks
  #has_many :sms
  validates_uniqueness_of :name , :scope=> :country

  attr_accessible :name,:country, :users_size, :measures_size, :tags_size

  scope :with_geodata , :select=>"distinct cities.*",
              :joins=>:tracks,
              :conditions=>"tracks.geolocated=true",
              :order=>"country, name"

  #by name - todo
  scope :by_name , :select=>"distinct cities.*",
        :conditions=>"cities.name like 'Brus%'",
        :order=>"country, name"
  #scope :best_cities, lambda{ |limit| select('count(*), city_name').joins(:tracks).joins(:measures).group('city_name') unless name.nil? }

  # Check the existance of a city's name using a geocoder
  def self.exist?(name)
    res=Geokit::Geocoders::MultiGeocoder.geocode("#{name}")
    return res.success
  end

  #best_cities - get the city with the most measures and most recent activity
  def self.best_cities(lim)
    all(:select=>"id, name, country, last_activity_at, measures_size, users_size, tracks_size, tags_size",
         :order=>"cities.measures_size desc, last_activity_at desc",
         :conditions => "last_activity_at is not null and measures_size is not null",
         :limit=>lim)
  end

  def contributors(limit)
      self.tracks.all( :select=>"user_id, max(created_at) as last",
                       :include=>:user,
                       :group=>"tracks.user_id",
                       :order=>"max(created_at)",
                       :limit=>limit)
  end

  #contributors, but with join
  def city_contributors(limit)
    self.tracks.all( :select=>"user_id, max(tracks.created_at) as last, login",
                     :joins=>:user,
                     :group=>"tracks.user_id, login",
                     :order=>"max(tracks.created_at)",
                     :limit=>limit)
  end

  # find city or build it if doesn't exist
  def self.find_or_build(city, country)
    #puts "Looking for city #{city}, #{country} in the database"
    #OLD VERSION (not aware of city_names table): city=City.find(:first,:conditions=>{:name=>name, :country=>country})
    city_name = CityNames.find(:first, :joins=>"JOIN cities ON city_names.city_id = cities.id", :conditions=>"city_names.name=#{city} and cities.country=#{county}" )
    unless city_name.nil?
      #puts "City found (id = #{city_name.city.id}"
      return city_name.city
    else
      #puts "City not found"
      s = Geocoder.search("#{city}, #{country}")
      return City.build_city(s, true) #force=true
    end
  end

  # Create a city using external geocoder data (to find its lat,lng)
  def self.build_city(s, force = false)
    city = City.first(:conditions=>["name= ? and country = ?", s[0].city, s[0].country_code]) unless force
    if force || city.nil?
      # create new city
      city = City.new(:name=>s[0].city, :country=>s[0].country_code)
      lat = s[0].latitude
      lng = s[0].longitude
      city.lat,city.lng=lat.to_f, lng.to_f
      city.save
    end
    return city
  end

  def validate

    errors.add(:location, "no location") if self.location.nil?

    unless self.location.match('(.*),(.*)')
      errors.add(:location, "badly formatted (city, country)")
    end

    unless City.exist?(self.location)
      errors.add(:location, "not found")
    end
  end

  def users_recorded
    User.find(:joins=>:tracks, :conditions=>"tracks.city_id=#{self.id}")
  end

  def users_recorded_count
    User.count(:joins=>:tracks, :conditions=>"tracks.city_id=#{self.id}")
  end

  def tags_count(context="tags")
    unless (context.nil?)
      context_sql= "and context='#{context}'"
    end
    #Tagging.count(:joins=>"LEFT OUTER JOIN measures ON measures.id = taggings.taggable_id LEFT OUTER JOIN tracks ON tracks.id = measures.track_id", :conditions=> "city_id=#{self.id} #{context_sql}")
    MeasurementsTag.count(:joins=>"INNER JOIN tracks ON tracks.id = measurements_tags.track_id", :conditions=> "tracks.city_id=#{self.id} #{context_sql}")
  end

  def tags(limit=100)
   #Tagging.find(:all, )
   #Measure.tag_counts(:joins=>"LEFT OUTER JOIN tracks ON tracks.id = measurements_tags.track_id ", :conditions=> "tracks.city_id=#{self.id}", :order=>"count desc", :limit=>limit)
   MeasurementsTag.tag_counts(:joins=>"INNER JOIN tracks ON tracks.id = measurements_tags.track_id", :conditions=> "tracks.city_id=#{self.id}", limit: limit)
  end

end
